<?php

namespace W3;

/**
 * 服务器响应处理类 (对应于Request，它代表了一个HTTP响应。这个对象包括了返回头，HTTP状态码和返回体。)
 *
 * @author edikud
 * @date 2022/10/22
 * @copyright Copyright (c) 2022 W3 (http://www.mcooo.com)
 * @license GNU General Public License 2.0
 *
 */
class Response
{
    /**
     * 存储所有已注册的标头,
     * 将随响应一起发送
     *
     * @var array
     */
    protected $headers = [];
	
    /**
     * @var int HTTP状态
     */
    protected $code = 200;

    /**
     * 响应的内容类型
     *
     * @var string
     */
    protected $type = 'text/html';

    /**
     * @var string HTTP响应主体
     */
    protected $body;

    /**
     * 内容类型字符集
     *
     * @var string
     */
    protected $charset = 'UTF-8';

    /**
     * @var bool 已发送HTTP响应标注
     */
    protected $sent = false;

    /**
     * @var array 状态代码
     */
    public static $messages = [
        100 => 'Continue',
        101 => 'Switching Protocols',
        102 => 'Processing',

        200 => 'OK',
        201 => 'Created',
        202 => 'Accepted',
        203 => 'Non-Authoritative Information',
        204 => 'No Content',
        205 => 'Reset Content',
        206 => 'Partial Content',
        207 => 'Multi-Status',
        208 => 'Already Reported',

        226 => 'IM Used',

        300 => 'Multiple Choices',
        301 => 'Moved Permanently',
        302 => 'Found',
        303 => 'See Other',
        304 => 'Not Modified',
        305 => 'Use Proxy',
        306 => '(Unused)',
        307 => 'Temporary Redirect',
        308 => 'Permanent Redirect',

        400 => 'Bad Request',
        401 => 'Unauthorized',
        402 => 'Payment Required',
        403 => 'Forbidden',
        404 => 'Not Found',
        405 => 'Method Not Allowed',
        406 => 'Not Acceptable',
        407 => 'Proxy Authentication Required',
        408 => 'Request Timeout',
        409 => 'Conflict',
        410 => 'Gone',
        411 => 'Length Required',
        412 => 'Precondition Failed',
        413 => 'Payload Too Large',
        414 => 'URI Too Long',
        415 => 'Unsupported Media Type',
        416 => 'Range Not Satisfiable',
        417 => 'Expectation Failed',

        422 => 'Unprocessable Entity',
        423 => 'Locked',
        424 => 'Failed Dependency',

        426 => 'Upgrade Required',

        428 => 'Precondition Required',
        429 => 'Too Many Requests',

        431 => 'Request Header Fields Too Large',

        500 => 'Internal Server Error',
        501 => 'Not Implemented',
        502 => 'Bad Gateway',
        503 => 'Service Unavailable',
        504 => 'Gateway Timeout',
        505 => 'HTTP Version Not Supported',
        506 => 'Variant Also Negotiates',
        507 => 'Insufficient Storage',
        508 => 'Loop Detected',

        510 => 'Not Extended',
        511 => 'Network Authentication Required'
    ];


    /**
     * 单例句柄
     *
     * @access protected
     * @var Response
     */
    protected static $instance;
	
    /**
     * 单例实例
     *
     * @return Response
     */
    public static function instance(): Response
    {
        if (null === self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * 创建新实例
     *
     * @access public
     * @return Response
     */
    public static function make(): Response
    {
        return new static();
    }

    /**
     * 设置响应的HTTP状态
     *
     * @param int $code HTTP状态代码
     * @return object|int Self reference
     * @throws Exception If invalid status code
     */
    public function code($code = null) 
	{
        if ($code === null) {
            return $this->code;
        }

        if (array_key_exists($code, static::$messages)) {
            $this->code = $code;
        }
        else {
			$this->code = 200;
            //throw new Exception('Invalid status code.');
        }

        return $this;
    }

    /**
     * 向响应添加标头
     *
     * @param string|array $name Header name or array of names and values
     * @param string $value Header value
     * @return object Self reference
     */
    public function header($name, $value = null) 
	{
        if (is_array($name)) {
            foreach ($name as $k => $v) {
                $this->headers[$k] = $v;
            }
        }
        else {
            $this->headers[$name] = $value;
        }

        return $this;
    }

    /**
     * Getter for the content type
     *
     * @return string
     */
    public function type($type = null)
    {
        if ($type === null) {
            return $this->type;
        }
		
		$this->type = $type;
		
		return $this;
    }

    /**
     * 内容
     *
     * @return string
     */
    public function body(): string
    {
        return $this->body;
    }

    /**
     * 内容类型字符集
     *
     * @return string
     */
    public function charset($charset = null)
    {
        if ($charset === null) {
            return $this->charset;
        }
		
		$this->charset = $charset;
		
		return $this;
    }

    /**
     * 返回响应的标头
     *
     * @return array
     */
    public function headers(): array
    {
        return $this->headers;
    }

    /**
     * 将内容写入响应正文
     *
     * @param string $str Response content
     * @return object Self reference
     */
    public function write($str) 
	{
        $this->body .= $str;

        return $this;
    }

    /**
     * 清除响应
     *
     * @return object Self reference
     */
    public function clear() 
	{
        $this->code = 200;
        $this->headers = [];
        $this->body = '';

        return $this;
    }

    /**
     * 抛出json回执信息
     *
     * @access public
     * @param string $message 消息体
     * @return void
     */
    public function json($data, $code = 200, $encode = true, $charset = 'utf-8', $option = NULL) 
	{
        $encode && $data = Json::encode($data, $option);
        $this->code($code)
		    ->charset($charset)
            ->type('application/json')
            ->write($data)
            ->send();
		exit;
    }

    /**
     * 发送HTTP头
     *
     * @return object Self reference
     */
    public function sendHeaders() 
	{
        // 发送状态代码标题
        if (strpos(php_sapi_name(), 'cgi') !== false) {
            header(
                sprintf(
                    'Status: %d %s',
                    $this->code,
                    static::$messages[$this->code]
                ),
                true
            );
        }
        else {
            header(
                sprintf(
                    '%s %d %s',
                    (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'),
                    $this->code,
                    static::$messages[$this->code]),
                true,
                $this->code
            );
        }
		
		$this->type && header('Content-Type: ' . $this->type . '; charset=' . $this->charset, true);

        // Send other headers
        foreach ($this->headers as $field => $value) 
		{
			# 是数组 false (允许相同类型的多个报头)。
            if (is_array($value)) {
                foreach ($value as $v) 
				{
                    header($field.': '.$v, false);
                }
            } else {
				
				# 默认是 true (替换)
                header($field.': '.$value);
            }
        }

        // set cookie
        foreach (Cookie::export() as $cookie) 
		{
            [$key, $value, $timeout, $path, $domain, $secure, $httponly] = $cookie;

            if ($timeout > 0) {
                $now = time();
                $timeout += $timeout > $now - 86400 ? 0 : $now;
            } elseif ($timeout < 0) {
                $timeout = 1;
            }

            setrawcookie($key, rawurlencode($value), $timeout, $path, $domain, $secure, $httponly);
        }

        return $this;
    }

    /**
     * 获取是否已发送响应
     */
    public function sent() 
	{
        return $this->sent;
    }

    /**
     * 发送HTTP响应.
     */
    public function send() 
	{
        if (ob_get_length() > 0) {
            ob_end_clean();
        }

        if (!headers_sent()) {
            $this->sendHeaders();
        }

        echo $this->body;

        $this->sent = true;
    }
}

